Skip to content

[deprecation] Add RenameDeprecatedMethodCallRector inferring rename from @deprecated docblock#8015

Merged
TomasVotruba merged 3 commits into
mainfrom
tv-rename-deprecated-method-call
Jun 16, 2026
Merged

[deprecation] Add RenameDeprecatedMethodCallRector inferring rename from @deprecated docblock#8015
TomasVotruba merged 3 commits into
mainfrom
tv-rename-deprecated-method-call

Conversation

@TomasVotruba

@TomasVotruba TomasVotruba commented May 30, 2026

Copy link
Copy Markdown
Member

Description

New core rule RenameDeprecatedMethodCallRector. It looks for method calls (MethodCall, StaticCall) where the called method carries a @deprecated annotation, infers a simple replacement method name from the docblock, and renames the call.

Example

Given a class whose method is @deprecated with a replacement hint:

final class SomeApiClient
{
    /**
     * @deprecated since 2.0, use fetchData() instead
     */
    public function getData(): array
    {
        return $this->fetchData();
    }

    public function fetchData(): array
    {
        return [];
    }
}

the rule rewrites calls to the deprecated method:

-$someApiClient->getData();
+$someApiClient->fetchData();

The same applies to static calls and the replaced by newMethod() / {@see newMethod()} docblock forms.

How it works

  1. Resolves the called method's reflection via ReflectionResolver.
  2. Hands the reflection to DeprecatedMethodCallReplacementResolver, which:
    • skips unless isDeprecated()->yes();
    • parses getDeprecatedDescription() for a replacement name in three common forms:
      • use newMethod() instead
      • replaced by newMethod()
      • {@see newMethod()}
    • Safety guard: only returns a name if the suggested method actually exists on the same declaring class, and is not itself deprecated (avoids dead-end renames).
  3. If a replacement name comes back, the call's method name is swapped.

Scope / limitations (intentional for this first cut)

  • Same-class method rename only; cross-class Other::newMethod() suggestions are skipped.
  • NullsafeMethodCall not handled (not yet supported by ReflectionResolver).
  • A standalone sibling @see tag (outside the @deprecated text) is not read; only inline {@see ...}.
  • Registered in the rule's test config only, not wired into any set.

Tests

6 rule fixtures (use-instead / replaced-by / {@see} / static call + 2 skips) backed by a Source/SomeApiClient fixture, plus a dedicated unit test for DeprecatedMethodCallReplacementResolver covering each docblock form and every bail-out path. ECS, PHPStan level 8, and Rector all pass on the new code.

@TomasVotruba TomasVotruba changed the title [Renaming] Add RenameDeprecatedMethodCallRector inferring rename from @deprecated docblock [deprecation] Add RenameDeprecatedMethodCallRector inferring rename from @deprecated docblock Jun 2, 2026
@TomasVotruba TomasVotruba self-assigned this Jun 2, 2026
Comment thread rules/Renaming/Rector/MethodCall/RenameDeprecatedMethodCallRector.php Outdated
Comment thread rules/Renaming/NodeAnalyzer/DeprecatedMethodCallReplacementResolver.php Outdated
@TomasVotruba TomasVotruba force-pushed the tv-rename-deprecated-method-call branch from 2058b15 to 548cec4 Compare June 16, 2026 09:53
@TomasVotruba

Copy link
Copy Markdown
Member Author

Let's ship this 👍

@TomasVotruba TomasVotruba merged commit 9704b6f into main Jun 16, 2026
64 checks passed
@TomasVotruba TomasVotruba deleted the tv-rename-deprecated-method-call branch June 16, 2026 09:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants